package com.example.score.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.score.common.RateConstants;
import com.example.score.common.ServiceException;
import com.example.score.entity.ClassInfo;
import com.example.score.entity.ExamBase;
import com.example.score.entity.input.SchoolBaseInput;
import com.example.score.entity.vo.*;
import com.example.score.mapper.ClassInfoMapper;
import com.example.score.mapper.ExamBaseMapper;
import com.example.score.mapper.StudentSubmitMapper;
import com.example.score.utils.DateUtils;
import com.example.score.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 校本资源统计作业情况
 */
@Slf4j
@Service
public class SchoolBasedCountService {

    @Resource
    private ExamBaseMapper examBaseMapper;

    @Resource
    private StudentSubmitMapper studentSubmitMapper;

    @Resource
    private ClassInfoMapper classInfoMapper;


    /**
     * 作业次数
     *
     * @param param 查询参数
     * @return 数据
     */
    public ExamCountVo examCount(SchoolBaseInput param) {
        ExamCountVo examCountVo = new ExamCountVo();
        //查询范围的数据
        int count = examBaseMapper.examCount(param);
        examCountVo.setCount(count);
        //今日查询条件
        SchoolBaseInput todayParam = copyParam(param);
        int today = examBaseMapper.examCount(todayParam);
        examCountVo.setToday(today);
        return examCountVo;
    }


    /**
     * 作业扫描次数
     *
     * @param param 查询参数
     * @return 数据
     */
    public ExamCountVo examScanCount(SchoolBaseInput param) {
        ExamCountVo examCountVo = new ExamCountVo();
        //查询范围的数据
        int count = studentSubmitMapper.studentSubmitCount(param);
        examCountVo.setCount(count);
        //今日查询条件
        SchoolBaseInput todayParam = copyParam(param);
        int today = studentSubmitMapper.studentSubmitCount(todayParam);
        examCountVo.setToday(today);
        return examCountVo;
    }


    /**
     * 构建作业分布数据
     * @param examVos 作业信息
     * @param date 日期
     * @return 作业分布数据
     */
    public ExamDistributeVo buildDistribute(List<ExamVo> examVos, List<String> date) {
        ExamDistributeVo examDistributeVo = new ExamDistributeVo();

        examDistributeVo.setDate(date);
        //根据年级分组
        Set<Map.Entry<Long, List<ExamVo>>> gradeGroup = examVos.stream().collect(Collectors.groupingBy(ExamVo::getGradeId)).entrySet();

        List<ExamDistributeGradeVo> gradeVos = gradeGroup.stream().map(entity -> {
            //年级id
            Long gradeId = entity.getKey();
            //年级分组鼩
            List<ExamVo> value = entity.getValue();
            ExamDistributeGradeVo examDistributeGradeVo = new ExamDistributeGradeVo();
            List<Long> count = new ArrayList<>();
            //每个月的学科分组
            List<List<ExamSubjectVo>> subjectList = new ArrayList<>();
            for (String d : date) {
                //每个月的总数
                List<ExamVo> mouthExamVo = value.stream().filter(v -> v.getStartTimeFormat().equals(d)).collect(Collectors.toList());
                //每个月的学科分组
                Set<Map.Entry<String, List<ExamVo>>> subjectGroup = mouthExamVo.stream()
                        .filter(e -> StringUtils.isNotEmpty(e.getSubjectName())).collect(Collectors.groupingBy(ExamVo::getSubjectName)).entrySet();
                List<ExamSubjectVo> subjectVos = subjectGroup.stream().map(e -> new ExamSubjectVo().setSubjectName(e.getKey()).setSubjectCount(e.getValue().size())).collect(Collectors.toList());
                subjectList.add(subjectVos);
                count.add((long) mouthExamVo.size());
            }
            examDistributeGradeVo.setCount(count);
            examDistributeGradeVo.setGradeId(gradeId);
            examDistributeGradeVo.setSubjectList(subjectList);
            return examDistributeGradeVo;
        }).collect(Collectors.toList());
        examDistributeVo.setGrade(gradeVos);
        examDistributeVo.setTotal(examVos.size());
        return examDistributeVo;
    }

    /**
     * 按天维度构建作业分布
     *
     * @param param 参数
     * @return 数据
     */
    public ExamDistributeVo buildDistributeForDay(SchoolBaseInput param, List<ExamVo> examVos) {
        List<String> days = DateUtils.listMonthDays(DateUtils.dateTime(param.getStartTime()));
        //按日格式化开始时间
        examVos = examVos.stream().filter(e -> !ObjectUtils.isEmpty(e.getStartTime()) && !ObjectUtils.isEmpty(e.getGradeId()))
                .peek(e -> e.setStartTimeFormat(e.getStartTime().format(DateUtils.FMT))).collect(Collectors.toList());
        return buildDistribute(examVos, days);
    }

    /**
     * 按月维度构建作业分布
     *
     * @param param 参数
     * @return 数据
     */
    public ExamDistributeVo buildDistributeForMonth(SchoolBaseInput param, List<ExamVo> examVos, List<String> months) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateUtils.YYYY_MM);
        //格式化开始时间
        examVos = examVos.stream().filter(e -> !ObjectUtils.isEmpty(e.getStartTime()) && !ObjectUtils.isEmpty(e.getGradeId()))
                .peek(e -> e.setStartTimeFormat(e.getStartTime().format(formatter))).collect(Collectors.toList());
        return buildDistribute(examVos, months);
    }

    /**
     * 作业分布
     *
     * @param param 参数
     * @return 分布情况
     */
    public ExamDistributeVo examDistribute(SchoolBaseInput param) {
        checkParam(param);
        List<String> months = DateUtils.getMonthsBetween(DateUtils.dateTime(param.getStartTime()), DateUtils.dateTime(param.getEndTime()));
        //获取月份数据
        ExamDistributeVo examDistributeVo = new ExamDistributeVo();
        examDistributeVo.setDate(months);
        //获取作业数据
        List<ExamVo> examVos = examBaseMapper.examList(param);
        if (CollectionUtils.isEmpty(examVos)) {
            return examDistributeVo;
        }
        //如果只有一个月，则基于天的维度计算
        if (months.size() == 1) {
            return buildDistributeForDay(param, examVos);
        }
        return buildDistributeForMonth(param, examVos, months);
    }

    public void checkParam(SchoolBaseInput param) {
        if (ObjectUtils.isEmpty(param)) {
            throw new ServiceException("请输入参数");
        }
        if (ObjectUtils.isEmpty(param.getStartTime()) || ObjectUtils.isEmpty(param.getEndTime())) {
            throw new ServiceException("开始时间，结束时间不能为空");
        }
    }


    public List<StudentSubmitVo> calculateRate(List<StudentSubmitVo> studentSubmitVos, List<ExamVo> examVos) {
        return studentSubmitVos.stream().peek(s -> {
            BigDecimal score = s.getScore();
            Optional<ExamVo> any = examVos.stream().filter(e -> e.getExamDetailId().equals(s.getExamDetailId())).findAny();
            BigDecimal rate = BigDecimal.ZERO;
            if (any.isPresent()) {
                ExamVo examVo = any.get();
                BigDecimal fullScore = examVo.getFullScore();
                if (!fullScore.equals(BigDecimal.ZERO)) {
                    //计算得分率
                    rate = score.divide(fullScore, RateConstants.DEFAULT_SCALE, RoundingMode.HALF_UP);
                }
                s.setFullScore(examVo.getFullScore());
            }
            s.setRate(rate);
        }).collect(Collectors.toList());
    }


    public BigDecimal levelRate(int submitSize, int levelCount) {
        if (submitSize == 0) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(String.valueOf(levelCount)).divide(new BigDecimal(String.valueOf(submitSize)),
                RateConstants.DEFAULT_SCALE, RoundingMode.HALF_UP);
    }

    /**
     * 学习情况列表
     *
     * @param param 查询参数
     * @return 数据
     */
    public List<ExamInfoVo> listExamInfo(SchoolBaseInput param) {
        //作业
        List<ExamVo> examVos = examBaseMapper.examList(param);
        if (CollectionUtils.isEmpty(examVos)) {
            return new ArrayList<>();
        }
        List<Long> examDetailIds = examVos.stream().map(ExamVo::getExamDetailId).collect(Collectors.toList());
        //班级数据
        List<ClassInfo> classInfoList = classInfoMapper.selectList(new LambdaQueryWrapper<ClassInfo>().in(ClassInfo::getExamDetailId, examDetailIds));
        //提交人数
        List<StudentSubmitVo> studentSubmitVos = studentSubmitMapper.listByExamDetailIds(examDetailIds);
        //计算得分率
        List<StudentSubmitVo> rateList = calculateRate(studentSubmitVos, examVos);
        //班级分组
        Set<Map.Entry<Long, List<ClassInfo>>> classGroup = classInfoList.stream().collect(Collectors.groupingBy(ClassInfo::getClassId)).entrySet();

        List<ExamInfoVo> list = classGroup.stream().map(e -> {
            Long classId = e.getKey();
            List<ClassInfo> classInfos = e.getValue();
            ClassInfo classInfo = classInfos.get(0);
            int studentCount = classInfos.stream().mapToInt(ClassInfo::getStudentCount).max().getAsInt();
            //班级下的作业
            List<Long> classExamDetailIds = classInfos.stream().map(ClassInfo::getExamDetailId).collect(Collectors.toList());
            //学生的提交记录
            List<StudentSubmitVo> submitVos = rateList.stream().filter(s -> s.getClassId().equals(classId)).collect(Collectors.toList());
            //作业次数
            long examCount = examVos.stream().filter(exam -> classExamDetailIds.contains(exam.getExamDetailId())).count();
            ExamInfoVo examInfoVo = new ExamInfoVo();
            examInfoVo.setClassId(classId);
            examInfoVo.setClassName(classInfo.getClassName());

            examInfoVo.setStudentCount(studentCount);
            examInfoVo.setExamCount((int) examCount);
            //计算每个级别的总数
            calculateCount(submitVos, examInfoVo);
            return examInfoVo;
        }).collect(Collectors.toList());
        //全班级人数
        int allStudentCount = list.stream().mapToInt(ExamInfoVo::getStudentCount).sum();
        ExamInfoVo all = new ExamInfoVo()
                .setClassName("全班级")
                .setStudentCount(allStudentCount)
                .setExamCount(examVos.size());
        //计算总数
        calculateCount(rateList, all);
        list = list.stream().sorted(Comparator.comparing(ExamInfoVo::getStudentCount).reversed()).collect(Collectors.toList());
        //全班级始终保持在第一个
        list.add(0, all);
        return list;
    }


    /**
     * 计算每个级别的总数
     *
     * @param submitVos 提交记录
     * @return 每个级别的总数
     */
    private void calculateCount(List<StudentSubmitVo> submitVos, ExamInfoVo examInfoVo) {
        int submitSize = submitVos.size();
        //优秀
        int excellentCount = (int) submitVos.stream().filter(r -> r.getRate().compareTo(RateConstants.EXCELLENT) > 0).count();
        //良好
        int goodCount = (int) submitVos.stream().filter(r -> r.getRate().compareTo(RateConstants.GOOD) > 0 &&
                r.getRate().compareTo(RateConstants.EXCELLENT) <= 0).count();
        //及格
        int passCount = (int) submitVos.stream().filter(r -> r.getRate().compareTo(RateConstants.PASS) > 0 &&
                r.getRate().compareTo(RateConstants.GOOD) <= 0).count();
        //低分
        int lowCount = (int) submitVos.stream().filter(r -> r.getRate().compareTo(RateConstants.LOW) < 0).count();
        examInfoVo.setSubmitCount(submitVos.size());
        examInfoVo.setExcellentCount(excellentCount);
        examInfoVo.setExcellentRate(levelRate(submitSize, excellentCount));
        examInfoVo.setGoodCount(goodCount);
        examInfoVo.setGoodRate(levelRate(submitSize, goodCount));
        examInfoVo.setPassCount(passCount);
        examInfoVo.setPassRate(levelRate(submitSize, passCount));
        examInfoVo.setLowCount(lowCount);
        examInfoVo.setLowRate(levelRate(submitSize, lowCount));
    }


    public List<ExamInfoExcelVo> dataToExcel(List<ExamInfoVo> data) {
        return data.stream().map(e -> {
            ExamInfoExcelVo examInfoExcelVo = new ExamInfoExcelVo();
            BeanUtils.copyProperties(e, examInfoExcelVo);
            examInfoExcelVo.setExcellentRate(RateConstants.multiplyOneHundred(e.getExcellentRate()));
            examInfoExcelVo.setGoodRate(RateConstants.multiplyOneHundred(e.getGoodRate()));
            examInfoExcelVo.setPassRate(RateConstants.multiplyOneHundred(e.getPassRate()));
            examInfoExcelVo.setLowRate(RateConstants.multiplyOneHundred(e.getLowRate()));
            return examInfoExcelVo;
        }).collect(Collectors.toList());
    }

    /**
     * 科目扫描量排行
     *
     * @param param 参数
     * @return 数据
     */
    public List<StudentSubmitSubjectVo> subjectRank(SchoolBaseInput param) {
        List<StudentSubmitVo> studentSubmitVos = studentSubmitMapper.studentSubmitList(param);
        if (CollectionUtils.isEmpty(studentSubmitVos)) {
            return new ArrayList<>();
        }
        //根据学科分组
        Set<Map.Entry<Long, List<StudentSubmitVo>>> subjectNameGroup = studentSubmitVos.stream()
                .filter(e -> StringUtils.isNotEmpty(e.getSubjectName()) && !ObjectUtils.isEmpty(e.getSubjectId()))
                .collect(Collectors.groupingBy(StudentSubmitVo::getSubjectId)).entrySet();

        return subjectNameGroup.stream().map(e -> {
                    List<StudentSubmitVo> value = e.getValue();
                    return new StudentSubmitSubjectVo()
                            .setSubjectId(e.getKey())
                            .setSubjectName(value.get(0).getSubjectName())
                            .setCount(value.size());
                }).sorted(Comparator.comparing(StudentSubmitSubjectVo::getCount).reversed())
                .collect(Collectors.toList());
    }


    public SchoolBaseInput copyParam(SchoolBaseInput resource) {
        SchoolBaseInput todayParam = new SchoolBaseInput();
        BeanUtils.copyProperties(resource, todayParam);
        todayParam.setStartTime(DateUtils.getTodayStartTime());
        todayParam.setEndTime(DateUtils.getTodayEndTime());
        return todayParam;
    }


    /**
     * 年份列表
     *
     * @param schoolId 学校id
     * @return 年份列表
     */
    public List<Integer> listYears(Long schoolId) {
        List<ExamBase> examBases = examBaseMapper.selectBySchoolId(schoolId);
        if (CollectionUtils.isEmpty(examBases)) {
            return new ArrayList<>();
        }
        return examBases.stream().map(e -> e.getStartTime().getYear())
                .distinct().collect(Collectors.toList());
    }
}
